
package com.bong.panpan.bongcharttools.ble;

import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattDescriptor;
import android.bluetooth.BluetoothGattService;
import android.bluetooth.BluetoothProfile;
import android.content.Context;

import java.util.ArrayList;
import java.util.UUID;

/**
 * Created by panpan on 2017/8/21.
 */

public class BleLink {
    public static String RX_SERVER_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca1e";
    public static String TX_SERVER_UUID = "6e400001-b5a3-f393-e0a9-e50e24dcca1e";
    public static String RX_UUID = "6e400002-b5a3-f393-e0a9-e50e24dcca1e";
    public static String TX_UUID = "6e400003-b5a3-f393-e0a9-e50e24dcca1e";
    public static final String DESC_UUID = "00002902-0000-1000-8000-00805f9b34fb";
    public int id = 0;
    private BluetoothGatt mBluetoothGatt;
    private BluetoothGattCharacteristic txChar, rxChar;
    private String address = "", name = "";
    private byte[] recivedData;
    private int rssi;
    private BluetoothGattCallback bleCallBack = new BluetoothGattCallback() {
        @Override
        public void onConnectionStateChange(BluetoothGatt gatt, int status, int newState) {

            if (status == BluetoothGatt.GATT_SUCCESS) {
                if (newState == BluetoothProfile.STATE_CONNECTING) {

                }
                if (newState == BluetoothProfile.STATE_CONNECTED) {
                    if (gatt.discoverServices()) {
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISCOVERING);
                    } else {
                        errorMsg = "发现服务失败！";
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKFAILED);
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
                    }
                }
                if (newState == BluetoothProfile.STATE_DISCONNECTED) {
                    if (deviceState == BleDeviceState.DEVICE_STATE_LINKING) {
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKFAILED);
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
                    }
                    if (deviceState == BleDeviceState.DEVICE_STATE_DISCOVERING) {
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKFAILED);
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
                    }
                    if (deviceState == BleDeviceState.DEVICE_STATE_LINKED) {
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKLOST);
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
                    }
                }
            } else {
                errorMsg = "status=" + String.format("0x%02X", status);
                if (deviceState == BleDeviceState.DEVICE_STATE_LINKED) {
                    OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKLOST);
                    OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
                } else {
                    OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKFAILED);
                    OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
                }
            }
        }

        @Override
        public void onServicesDiscovered(BluetoothGatt gatt, int status) {
            if (status == BluetoothGatt.GATT_SUCCESS) {
                BluetoothGattService bleServer = gatt.getService(UUID.fromString(TX_SERVER_UUID));
                if (bleServer != null) {
                    OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_CHARACTER);
                    txChar = bleServer.getCharacteristic(UUID.fromString(TX_UUID));
                    if (txChar != null) {
                        if (mBluetoothGatt.setCharacteristicNotification(txChar, true)) {

                        } else {
                            errorMsg = "通知使能失败：mBluetoothGatt.setCharacteristicNotification(txChar, true)";
                            OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKFAILED);
                            OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
                            return;
                        }
                        BluetoothGattDescriptor descriptor = txChar.getDescriptor(
                                UUID.fromString(DESC_UUID));
                        descriptor.setValue(BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE);
                        if (mBluetoothGatt.writeDescriptor(descriptor)) {

                        } else {
                            errorMsg = "通知使能失败：mBluetoothGatt.writeDescriptor(descriptor)";
                            OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKFAILED);
                            OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
                        }

                    } else {
                        errorMsg = "未发现TX特性：" + TX_UUID;
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKFAILED);
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
                        return;
                    }
                } else {
                    errorMsg = "未发现TX服务";
                    OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKFAILED);
                    OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
                    return;
                }
                if (!RX_SERVER_UUID.equals(TX_SERVER_UUID))
                    bleServer = gatt.getService(UUID.fromString(RX_SERVER_UUID));
                if (bleServer != null) {
                    OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_CHARACTER);
                    rxChar = bleServer.getCharacteristic(UUID.fromString(RX_UUID));
                    if (rxChar != null) {
                        rxChar.setWriteType(BluetoothGattCharacteristic.WRITE_TYPE_NO_RESPONSE);
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKED);
                    } else {
                        errorMsg = "未发现RX特性：" + RX_UUID;
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKFAILED);
                        OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
                        return;
                    }
                } else {
                    errorMsg = "未发现RX服务";
                    OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKFAILED);
                    OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
                    return;
                }
            } else {
                errorMsg = "发现服务操作失败";
                OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKFAILED);
                OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
            }
        }

        @Override
        public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicRead(gatt, characteristic, status);
        }

        @Override
        public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
            super.onCharacteristicWrite(gatt, characteristic, status);
        }

        @Override
        public void onCharacteristicChanged(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic) {
            if (characteristic == txChar) {
                byte data[] = characteristic.getValue();
                recivedData = data;
                if (data != null)
                    OnDataReceived(data);
            }
        }

        @Override
        public void onDescriptorRead(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorRead(gatt, descriptor, status);
        }

        @Override
        public void onDescriptorWrite(BluetoothGatt gatt, BluetoothGattDescriptor descriptor, int status) {
            super.onDescriptorWrite(gatt, descriptor, status);
        }

        @Override
        public void onReliableWriteCompleted(BluetoothGatt gatt, int status) {
            super.onReliableWriteCompleted(gatt, status);
        }

        @Override
        public void onReadRemoteRssi(BluetoothGatt gatt, int rssi, int status) {
            super.onReadRemoteRssi(gatt, rssi, status);
            BleLink.this.rssi = rssi;
        }

        @Override
        public void onMtuChanged(BluetoothGatt gatt, int mtu, int status) {
            super.onMtuChanged(gatt, mtu, status);
        }
    };

    public String getErrorMsg() {
        return errorMsg;
    }

    public void addBleListener(BleListener bleListener) {
        if (this.bleListener.indexOf(bleListener) == -1)
            this.bleListener.add(bleListener);
    }

    public void removeBleListener(BleListener bleListener) {
        this.bleListener.remove(bleListener);
    }

    public void removeBleListener() {
        this.bleListener.clear();
    }

    public void clearBleListener() {
        this.bleListener.clear();
    }

    public String getAddress() {
        return address;
    }

    public String getName() {
        return name;
    }

    private String errorMsg = "";
    private ArrayList<BleListener> bleListener = new ArrayList<>();
    private Context context;
    private BleDeviceState deviceState = BleDeviceState.DEVICE_STATE_DISLINK;

    public BleLink(Context context) {
        this.context = context;
    }

    public boolean link(BluetoothDevice device) {
        errorMsg = "";
        address = device.getAddress();
        name = device.getName();
        mBluetoothGatt = device.connectGatt(context, false, bleCallBack);
        if (mBluetoothGatt != null) {
            OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKING);
            return true;
        } else {
            OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_LINKFAILED);
            OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
        }
        return false;
    }

    public synchronized void dislink() {
        if (deviceState != BleDeviceState.DEVICE_STATE_DISLINK) {
            if (mBluetoothGatt != null) {
                mBluetoothGatt.disconnect();
                if (mBluetoothGatt != null)
                    mBluetoothGatt.close();
                mBluetoothGatt = null;
            }
            OnDeviceStateChanged(BleDeviceState.DEVICE_STATE_DISLINK);
        }
    }

    /**
     * @return true 发送成功
     */
    public boolean sendData(byte... data) {
        if (deviceState == BleDeviceState.DEVICE_STATE_LINKED) {
            rxChar.setValue(data);
            return mBluetoothGatt.writeCharacteristic(rxChar);
        }
        return false;
    }

    public byte[] readData() {
        byte[] result = recivedData;
        recivedData = null;
        return result;
    }

    public int getRssi() {
        rssi=0;
        if (deviceState == BleDeviceState.DEVICE_STATE_LINKED&&mBluetoothGatt.readRemoteRssi()) {
            for (int i = 0; i < 500; i++) {
                if(rssi!=0)
                    break;
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    break;
                }
            }
        }
     return rssi;
    }

    void OnDeviceStateChanged(BleDeviceState state) {
        if (this.deviceState != state) {
            deviceState = state;
            if (state == BleDeviceState.DEVICE_STATE_DISLINK && mBluetoothGatt != null) {
                mBluetoothGatt.close();
                mBluetoothGatt = null;
            }
            for (BleListener listener : bleListener) {
                listener.OnDeviceStateChanged(this, state);
            }
        }
    }

    void OnDataReceived(byte data[]) {
        for (BleListener listener : bleListener) {
            listener.OnDataReceived(this, data);
        }
    }

    public interface BleListener {
        void OnDeviceStateChanged(BleLink bleLink, BleDeviceState state);

        void OnDataReceived(BleLink bleLink, byte data[]);
    }

    public BleDeviceState getDeviceState() {
        return deviceState;
    }
}
